////////////////////////////////////////////////////////////////////////////////
//
//  The MIT License
//
//  Copyright (c) 2008 - 2009. Dimarik
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////

package tx.framework.service {

	import flash.events.AsyncErrorEvent;
	import flash.events.Event;
	import flash.events.SecurityErrorEvent;
	import flash.events.StatusEvent;
	import flash.net.LocalConnection;

	import tx.framework.logging.Log;
	import tx.framework.logging.LogEventLevel;
	import tx.framework.utils.ObjectUtil;

	/**
	 *  @author				Dimarik
	 *  @version			1.0
	 *  @langversion		3.0
	 *  @playerversion		9.0
	 *
	 */
	public class FBJSBridgeService implements IService {

		//---------------------------------------------------------------------
		//
		//   Public class's constants
		//
		//---------------------------------------------------------------------

		//-----------------------------
		// API methods
		//-----------------------------

		//-----------------------------
		// API methods argument's names
		//-----------------------------

		//---------------------------------------------------------------------
		//
		//   Public class's variables
		//
		//---------------------------------------------------------------------

		//---------------------------------------------------------------------
		//
		//   Private class's constants
		//
		//---------------------------------------------------------------------

		//---------------------------------------------------------------------
		//
		//   Private class's variables
		//
		//---------------------------------------------------------------------

		/**
		 * @private
		 */
		private static var _instance:		FBJSBridgeService;

		/**
		 * @private
		 */
		private static var _instantiated:	Boolean;

		//---------------------------------------------------------------------
		//
		//   Class's methods
		//
		//---------------------------------------------------------------------

		public static function getInstance():FBJSBridgeService {
			if (!_instance) {
				_instance = new FBJSBridgeService();
			}
			return _instance;
		}

		//---------------------------------------------------------------------
		//
		//   Constructor
		//
		//---------------------------------------------------------------------

		/**
		 * Constructor
		 */
		public function FBJSBridgeService() {
			super();
		}

		//---------------------------------------------------------------------
		//
		//   Private variables
		//
		//---------------------------------------------------------------------

		/**
		 * @private
		 */
		private var _remotePort:	String = '';

		/**
		 * @private
		 */
		private var _localPort:		String = '';

		/**
		 * @private
		 */
		private var _localLC:		LocalConnection;

		/**
		 * @private
		 */
		private var _remoteLC:		LocalConnection;

		/**
		 * @private
		 */
		private const _queue:		Array = new Array();

		/**
		 * @private
		 */
		private var _currentMethod:	String;

		/**
		 * @private
		 */
		private var _currentRequest:	Object;

		//---------------------------------------------------------------------
		//
		//   Interface implementation: IService
		//
		//---------------------------------------------------------------------
		
		public function configurate(params:Array):void {

			if (params.length < 2 || !params[0] || !params[1]) {
				throw new ArgumentError('Need both: remote and local ports');
				return;
			}

			this._remotePort = params[0];
			this._localPort = params[1];

			this._remoteLC = new LocalConnection();
			this._remoteLC.addEventListener(StatusEvent.STATUS,					this.handler_remoteNetStatus);
			this._remoteLC.addEventListener(AsyncErrorEvent.ASYNC_ERROR,		this.handler_fault);
			this._remoteLC.addEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_fault);

			this._localLC = new LocalConnection();
			this._localLC.allowDomain('apps.facebook.com', 'apps.*.facebook.com');
			this._localLC.addEventListener(StatusEvent.STATUS,					this.handler_localNetStatus);
			this._localLC.addEventListener(AsyncErrorEvent.ASYNC_ERROR,			this.handler_fault);
			this._localLC.addEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_fault);

			try {
				this._localLC.connect(this._localPort);
			} catch (error:Error) {
				throw new Error('FBJSBridgeService. Cant connect: ' + error.toString());
			}
		}

		//------------------------------------------
		// Common Remote Procedure Call
		//------------------------------------------
		public function call(apiMethod:String, responder:Object, options:Object = null):void {
			if (!this._remoteLC) {
				return;
				//throw new IllegalOperationError('FBJSBridgeService not configured');
			}

			var requestObject:Object = new Object();
			requestObject.responder = responder;
			requestObject.method = apiMethod;
			requestObject.options = options;

			this._currentMethod = apiMethod;

			this._queue.push(requestObject);

			if( !this._currentRequest ) this.requestNext();
		}

		//------------------------------------------
		//  Load data
		//------------------------------------------
		public function load(responder:Object, options:Object = null):void {

		}

		/**
		 * @param value	method name
		 * 
		 * @return 		reference to particular service method
		 */
		public function getParticularMethod(value:String):Function {
			if (this[value] is Function) {
				return this[value] as Function;
			}
			return null;
		}

		/**
		 * @param value	method name
		 * 
		 * @return 		has method
		 */
		public function hasParticularMethod(value:String):Boolean {
			return this[value] is Function;
		}

		//------------------------------------------
		// Particular Remote Procedure Call methods
		//------------------------------------------

		//---------------------------------------------------------------------
		//
		//   Private methods
		//
		//---------------------------------------------------------------------

		/**
		 * @private
		 */
		private function requestNext():void {
			if (this._queue.length == 0) return;

			this._currentRequest = this._queue.shift();

			var apiMethod:	String = this._currentRequest.method;
			var options:	Array = this._currentRequest.options;

			var arr:Array = new Array(LogEventLevel.INFO, apiMethod + ' ' + ObjectUtil.toString(options));
			Log.getLogger("FBJS-OUT").log.apply(null, arr);

			if (apiMethod =='navigateToURL') {
				arr = new Array(this._remotePort, 'navigateToURL', options as Array);
			} else {
				arr = new Array(this._remotePort, 'callFBJS', apiMethod, options as Array);
			}

			this._remoteLC.send.apply(this, arr);
	    }

		//---------------------------------------------------------------------
		//
		//   Event handlers
		//
		//---------------------------------------------------------------------

		/**
		 * @private
		 */
		protected function handler_fault(event:Event):void {
			trace('FBJSBridgeService handler_fault: ' + event.toString());
			this._currentRequest.responder.fault(event);
			this.requestNext();
		}

		/**
		 * @private
		 */
		private function handler_remoteNetStatus(event:StatusEvent):void {
			//trace('FBJSBridgeService handler_remoteNetStatus: ' + event.toString());

			var level:String = event.level;

			switch (level) {
				case 'status':
					this._currentRequest.responder.result(level);
					break;

				case 'error':
				default:
					this._currentRequest.responder.fault(event);
			}

			this._currentRequest = null;
			this.requestNext();
		}

		/**
		 * @private
		 */
		private function handler_localNetStatus(event:Event):void {
			trace('FBJSBridgeService handler_localNetStatus: ' + event.toString());
		}

	}
}
